﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using VMExplorer.Win32;

namespace VMExplorer.MemoryObjects
{
  /// <summary>
  /// Process environment block
  /// </summary>
  class PEBObject : RemoteMemoryObject<Native.PEB>
  {
    #region Construction

    private PEBObject(SafeProcessHandle hProcess, MemoryAddress baseAddress)
      : base(hProcess, baseAddress, RegionUsage.Process)
    {
      Name = "Process Environment Block";
    }

    public static IEnumerable<IMemoryObject> Create(SafeProcessHandle hProcess, MemoryAddress baseAddress)
    {
      List<IMemoryObject> memObjects = new List<IMemoryObject>();
      if (baseAddress.IsValid)
      {
        #region :: PEB ::

        PEBObject peb = new PEBObject(hProcess, baseAddress);
        memObjects.Add(peb);

        #endregion
        #region :: Heaps ::

        try
        {
          MemoryAddress[] heaps = peb.ProcessHeaps;
          for (int i = 0; i < heaps.Length; i++)
          {
            memObjects.AddRange(HeapObject.Create(hProcess, heaps[i], string.Format("Heap #{0}", i)));
          }

          memObjects.AddRange(HeapObject.Create(hProcess, new MemoryAddress(peb.Inner.ReadOnlySharedMemoryHeap), "Read-only shared heap"));

          HeapObject.TryIdentification(hProcess, memObjects);
        }
        catch
        {
        }

        #endregion
        #region :: Modules ::

        try
        {
          memObjects.AddRange(LdrDataObject.Create(hProcess, new MemoryAddress(peb.Inner.LoaderData)));
        }
        catch
        {
        }

        #endregion
        #region :: Process Parameters ::

        try
        {
          memObjects.AddRange(ProcessParametersObject.Create(hProcess, new MemoryAddress(peb.Inner.ProcessParameters)));
        }
        catch
        {
        }

        #endregion
        #region :: Other objects ::

        try
        {
          memObjects.Add(new CodePageObject(hProcess, peb.Inner.AnsiCodePageData, "ANSI codepage"));
          memObjects.Add(new CodePageObject(hProcess, peb.Inner.OemCodePageData, "OEM codepage"));
          memObjects.Add(new CodePageObject(hProcess, peb.Inner.UnicodeCaseTableData, "Unicode case table"));
        }
        catch
        {
        }

        try
        {
          memObjects.Add(new GDIHandlesObject(hProcess, peb.Inner.GdiSharedHandleTable));
        }
        catch
        {
        }

        try
        {
          memObjects.AddRange(ActxObject.Create(hProcess, peb.Inner.ActivationContextData, "Actx Data"));
          memObjects.AddRange(ActxObject.Create(hProcess, peb.Inner.SystemDefaultActivationContextData, "System Default Actx Data"));
        }
        catch
        {
        }

        #endregion
      }
      return memObjects;
    }

    #endregion

    private MemoryAddress[] ProcessHeaps
    {
      get
      {
        MemoryAddress[] r = new MemoryAddress[Inner.NumberOfHeaps];
        MemoryAddress currentAddress = new MemoryAddress(Inner.ProcessHeaps);
        for (int i = 0; i < Inner.NumberOfHeaps; i++)
        {
          if (!currentAddress.IsValid) break;
          r[i] = RemoteReader.ReadIntPtr(ProcessHandle, currentAddress);
          currentAddress += 4;
        }
        return r;
      }
    }
  };

}
